package com.zengdada.authority.service.impl;

import com.zengdada.authority.config.security.intercept.AuthorityUtil;
import com.zengdada.authority.model.AppUser;
import com.zengdada.authority.model.Dml;
import com.zengdada.authority.model.DmlColumn;
import com.zengdada.authority.service.DmlAuthorityService;
import com.zengdada.authority.service.DmlColumnAuthorityService;
import com.zengdada.authority.service.EnhanceBaseService;
import com.zengdada.authority.utils.CommonUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.jdbc.support.rowset.SqlRowSetMetaData;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.weekend.Fn;
import tk.mybatis.mapper.weekend.WeekendSqls;
import tk.mybatis.mapper.weekend.reflection.Reflections;

import java.lang.reflect.Field;
import java.sql.Types;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Auther: 12614
 * @Contact: 1261438511@qq.com
 * @Date: 2019-02-20 17:28
 * @Description:
 */
@Service
public class DmlColumnAuthorityServiceImpl extends EnhanceBaseService<DmlColumn> implements DmlColumnAuthorityService {

    @Autowired
    @Qualifier("dmlAuthorityServiceImpl")
    DmlAuthorityService dmlAuthorityService;
    @Autowired
    JdbcTemplate jdbcTemplate;
    @Autowired
    AuthorityUtil authorityUtil;

    static final Map<String, Integer> COLUMN_TYPE = new HashMap<String, Integer>() {{
        put("ANY_WHERE", -1);
        put("SELECT", 0);
        put("IN", 1);
        put("OUT", 2);
        put("INOUT", 3);
        put("RET_VALUE", 4);

    }};
    static final Map<String, Integer> ORACLE_TYPE = new HashMap<>();

    static {
        Field[] fields = Types.class.getDeclaredFields();
        try {
            for (Field field : fields)
                if (field.getType().getName().equals("int"))
                    ORACLE_TYPE.put(field.getName().toLowerCase(), field.getInt(null));
        } catch (Exception e) {
        }
    }

    @Transactional
    @Override
    public void autoGenerationColumn(String dmlId, boolean tag) {
        if (tag)
            if (mapper.selectCountByExample(new Example.Builder(DmlColumn.class).where(WeekendSqls.<DmlColumn>custom().andEqualTo(DmlColumn::getDmlId, dmlId)).build()) > 0)
                return;
        AppUser user = authorityUtil.getLoginUser();
        Dml dml = dmlAuthorityService.selectById(dmlId);
        if (dml == null || StringUtils.isEmpty(dml.getRawDml()))
            throw new RuntimeException("初始化列信息失败，请检查参数或者数据语句！");
        SqlRowSet rowSet;
        if (dml.getDmlType() != -1 && dml.getDmlType() != 0)
            throw new RuntimeException("初始化列信息失败，只有 静态查询 或者 anyWhere查询 才能初始化列信息！");
        try {
            String sql = (dml.getRawDml().trim().charAt(dml.getRawDml().trim().length() - 1) == ';' ? dml.getRawDml().trim().substring(0, dml.getRawDml().trim().length() - 1) : dml.getRawDml().trim()).trim();
            if (dml.getDmlType() == -1) sql = sql.replace("anyWhere", "");
            rowSet = jdbcTemplate.queryForRowSet(String.format("SELECT MY_TMP_TABLE.* FROM ( %s) MY_TMP_TABLE WHERE ROWNUM = 0 ", sql));
        } catch (DataAccessException e) {
            throw new RuntimeException("初始化列信息失败(只能初始化能直接执行的语句)，数据语句语法错误！！" + e.getMessage());
        }
        List<DmlColumn> columns = mapper.selectByExample(new Example.Builder(DmlColumn.class)
                .select(Reflections.fnToFieldName((Fn<DmlColumn, Object>) DmlColumn::getId), Reflections.fnToFieldName((Fn<DmlColumn, Object>) DmlColumn::getName))
                .where(WeekendSqls.<DmlColumn>custom().andEqualTo(DmlColumn::getDmlId, dmlId)).build());
//        List<String> fields = cColss.stream().map(e -> e.getField()).collect(Collectors.toList());
        DmlColumn column;
//        CEdit cEdit;

        SqlRowSetMetaData metaData = rowSet.getMetaData();
        for (int i = 1; i <= metaData.getColumnCount(); i++) {
            String field = metaData.getColumnName(i).toLowerCase();
            try {
                column = columns.stream().filter(e -> field.equals(e.getName().toLowerCase())).collect(Collectors.toList()).get(0);
                column.setDataType(metaData.getColumnClassName(i));
                column.setExtype1(getOracleType(metaData.getColumnType(i)));
                column.setIsValid(1);
                column.setModifyUser(user.getId());
                column.setModifyTime(new Date());
                mapper.updateByPrimaryKeySelective(column);
            } catch (Exception e) { // 表示原来没有
                column = new DmlColumn();
                column.setAttrs(512L + 32L);
                column.setDmlId(dmlId);
                column.setName(field);
                column.setLabel(metaData.getColumnLabel(i));
                column.setCaption(field.toUpperCase());
                column.setColumnIndex(i * 10);
                column.setColumnType(getColumnType(dml.getDmlType()));
                column.setDataType(metaData.getColumnClassName(i));
                column.setExtype1(getOracleType(metaData.getColumnType(i)));
                column.setModifyUser(user.getId());
                mapper.insertSelective(column);
            }
//            if (cTables.getIsSimple() == 1) { // 设置字段编辑信息
//                cEdit = cEditMapper.selectByPrimaryKey(cCols.getId());
//                if (cEdit == null) {
//                    cEdit = new CEdit();
//                    cEdit.setColsId(cCols.getId());
//                    cEdit.setEditType(cCols.getFieldType());
//                    cEditMapper.insertSelective(cEdit);
//                } else {
//                    cEdit.setEditType(cCols.getFieldType());
//                    cEditMapper.updateByPrimaryKey(cEdit);
//                }
//            }
        }
    }

    @Override
    public Map<String, Integer> getColumnTypes(String dmlId) {
        Map<String, Integer> result;
        Dml dml = dmlAuthorityService.selectById(dmlId);
        if (dml != null && (dml.getDmlType() == 0 || dml.getDmlType() == 1))
            result = new HashMap<String, Integer>() {{
                put("SELECT", 0);
                put("IN", 1);
            }};
        else if (dml != null && dml.getDmlType() == -1)
            result = new HashMap<String, Integer>() {{
                put("SELECT", 0);
                put("IN", 1);
                put("ANY_WHERE", -1);
            }};
        else
            result = new HashMap<>(COLUMN_TYPE);
        return result;
    }

    @Override
    public Map<String, Integer> getOracleTypes() {
        return new HashMap<>(ORACLE_TYPE);
    }

    @Override
    public int save(DmlColumn dmlColumn) {
        AppUser user = authorityUtil.getLoginUser();
        dmlColumn.setModifyUser(user.getId());
        dmlColumn.setModifyTime(new Date());
        if (StringUtils.isBlank(dmlColumn.getId())) {
            try {
                CommonUtils.reflectObjectTransString(dmlColumn, false);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            }
            return this.add(dmlColumn);
        } else return this.updateById(dmlColumn);
    }

    @Override
    public int batchDelete(String id) {
        if (StringUtils.isBlank(id)) return 0;
        return mapper.deleteByExample(new Example.Builder(DmlColumn.class).where(WeekendSqls.<DmlColumn>custom().andIn(DmlColumn::getId, Arrays.stream(id.split(",")).collect(Collectors.toList()))).build());
    }

    private int getColumnType(int type) {
        switch (type) {
            case -1:
            case 0:
            case 1:
            case 4:
                return 0;
            case 2:
                return 1;
            case 3:
                return 0;
            case 5:
            case 6:
            case 7:
                return 1;
            default:
                return 0;
        }
    }

    private String getOracleType(int type) {
        for (Map.Entry<String, Integer> entry : ORACLE_TYPE.entrySet()) {
            if (entry.getValue() == type) return entry.getKey();
        }
        return "varchar";
    }

    private String getJavaType(String fieldType) {
        switch (fieldType) {
            case "VARCHAR2":
            case "CLOB":
                return "string";
            case "NUMBER":
                return "int";
            case "DATE":
                return "date";
            default:
                return "string";
        }
    }


}
